import java.util.*;

import common.*;

public class Initializer extends Thread
{
	// only parameters
	static int n_resource_per_proc = 5;		// 1, 5, 10
	
	
	// turn on / off
	static boolean simulation = true;
	
	
	// fixed parameter
	static int n_task = 50;
	static String type = "r";	// "w", "r", "rw"
	
	
	static String protocol;
	static String [] IPs;
	static int [] numNodes;
	static int groupNumber;
	static ArrayList<Process> localProcesses;
	static Scanner scan;
	
	public static void main(String [] args)
	{
		Conf.N_RESOURCES_PER_NODE = n_resource_per_proc;	// 1, 5, 10
		
		Monitor console;
		
		scan = new Scanner(System.in);
		if(args.length != 6)
		{
			System.out.println("Usage Error: java Initializer [protocol] <group1IP> <number of nodes> <group2IP> <number of nodes> myGroup");
			System.exit(0);
		}
		
		protocol = args[0];
		if( protocol.toUpperCase().equals("RA") || protocol.toUpperCase().equals("TL") )	Conf.ME_SCHEME = Def.ME_TOKENLESS;
		else if ( protocol.toUpperCase().equals("R") || protocol.toUpperCase().equals("T") )	Conf.ME_SCHEME = Def.ME_TOKEN;
		else
			System.out.println("Usage Error: [protocol] should be TL or T (TL = Tokenlss, T = Token-based)");
		
		IPs = new String[2];
		IPs[0] = args[1];
		numNodes = new int[2];
		numNodes[0] = Integer.parseInt(args[2]);
		IPs[1] = args[3];
		//Initialize numNodes[1] to the sum of the nodes for easier use later.
		numNodes[1] = Integer.parseInt(args[4])+numNodes[0];
		groupNumber = Integer.parseInt(args[5])-1;
		
		MessagePasser.partnerIPs = new ArrayList<String>();
		MessagePasser.partnerPorts = new ArrayList<Integer>();
		//Build the network topology
		for(int i = 0; i < numNodes[0]; i++)
		{
			MessagePasser.partnerIPs.add(i, IPs[0]);
			MessagePasser.partnerPorts.add(i, (Conf.STARTING_PORT_NO+i));
		}
		for(int i = numNodes[0]; i < numNodes[1]; i++)
		{
			MessagePasser.partnerIPs.add(i, IPs[1]);
			MessagePasser.partnerPorts.add(i, (Conf.STARTING_PORT_NO+i));
		}
		MessagePasser.network_size = MessagePasser.partnerIPs.size();  
		
		//Build the local processes information
		localProcesses = new ArrayList<Process>();
		if(groupNumber == 1)
		{
			Utils.print_log("Initializing this process to " + numNodes[0] + " through " + numNodes[1]); 
			for(int i = numNodes[0]; i < numNodes[1]; i++)
			{
				Utils.print_log( "-init: instantiate proc(" + i + ")" );
				localProcesses.add((i - numNodes[0]), new Process(i, Conf.THREAD_SLEEPTIME_MS));	// sleepTime));

				// charmgil added (start)
                if( i == numNodes[0]){
                	int spinlock = Conf.THREAD_SAFE_SPINLOCK_COUNT;
                	while( spinlock-- != 0 );	// busy lock
                	//localProcesses.get(i - numNodes[0]).get_shell().turnon();
                	localProcesses.get(i - numNodes[0]).captain = true;
                	Utils.print_log( "-init: monitor on - okay **" );
                }
                // charmgil added (end)
			}
		}
		else
		{
			Utils.print_log("Initializing this process to 0 through " + (numNodes[0]-1));
			for(int i = 0; i < numNodes[0]; i++)
			{
				Utils.print_log( "-init: instantiate proc(" + i + ")" );
				localProcesses.add(i, new Process(i, Conf.THREAD_SLEEPTIME_MS));	// sleepTime));

				// charmgil added (start)
                if( i == 0 ){
                	int spinlock = Conf.THREAD_SAFE_SPINLOCK_COUNT;
                	while( spinlock-- != 0 );	// busy lock
                	//localProcesses.get(i).get_shell().turnon();
                	localProcesses.get(i).captain = true;
                	Utils.print_log( "-init: monitor on - okay **" );
                }
                // charmgil added (end)
			}
		}
		
		System.out.println("Finished building all local nodes, press enter when other initializers are here.");
		scan.nextLine();
    	
		for(int i = 0; i < localProcesses.size(); i++)
		{
			for(int j = 0; j < MessagePasser.partnerPorts.size(); j++)
			{
				if(i!=j)
				{
					localProcesses.get(i).addPartner(j);
				}
			}
		}
		
		// for membership mgmt - Raymond's tree
		MessagePasser.participants = new ArrayList<Integer>();
		MessagePasser.local_procs = new ArrayList<Integer>();
		for( int i = 0; i < MessagePasser.network_size; i++ ){
			MessagePasser.participants.add( i );
			
			if( groupNumber == 0 && i < numNodes[0] )
				MessagePasser.local_procs.add( i );
			else if( groupNumber == 1 && i >= numNodes[0] )
				MessagePasser.local_procs.add( i );
		}
		
		System.out.println("Finished building node graph.");
		scan.nextLine();

		
		// proc starts
		for( Process p : localProcesses ){
			p.start();
		}
		
		System.out.println( "autoexec()..." );
			
		
		// management console (for the experiment)
		if( Conf.MGMT_CONSOLE ){
			console = new Monitor( localProcesses );
			console.start();
		}
		
		Initializer x = new Initializer();
		x.start();
		
	}//end-of-main()
	
	public void run(){
		// simulation
		if( simulation && groupNumber == 0 ){
			
			// simul
			System.out.println( "Simulation starts (" + numNodes[1] + ")" );
			scan.nextLine();
			
			int n_node = numNodes[1] - 1;
			
			// proc
			int loop = n_task;
			int a,b,c;
			while( loop-- != 0 ){
				do{
					double temp = ( new Random() ).nextGaussian() / 2 + 1;
					a = ( int )Math.round( ( temp * (numNodes[0]-1) ) );
				}while( a < 0 || numNodes[0]-1 < a );
				do{
					double temp = ( new Random() ).nextGaussian() / 2 + 1;
					b = ( int )Math.round( ( temp * n_node ) );
				}while( a == b || n_node < b || b < 0 );
				
				do{
					double temp = ( new Random() ).nextGaussian() / 2 + 1;
					c = ( int )( temp * Conf.N_RESOURCES_PER_NODE - 1 );
				}while( c < 0 || c > Conf.N_RESOURCES_PER_NODE - 1 );
				
				String res_name = "file_" + b + "_" + c + ".txt"; 
				System.out.println( ( n_task - loop ) + ":" + a + " is sending req to " + b + " --- " + res_name );
				
				// msg
				{	// read
					int msg_type;
					if( type.equals( "r" ) ) msg_type = Def.REQUEST_READ;
					else if( type.equals( "w" ) ) msg_type = Def.REQUEST_WRITE;
					else{
						// 25% WR + 75% RD
						if( loop % 4 == 0 ) msg_type = Def.REQUEST_WRITE;
						else msg_type = Def.REQUEST_READ;
//						// 50% WR + 50% RD
//						if( loop % 2 == 0 ) msg_type = Def.REQUEST_WRITE;
//						else msg_type = Def.REQUEST_READ;
						// 75% WR + 25% RD
//						if( loop % 4 == 0 ) msg_type = Def.REQUEST_READ;
//						else msg_type = Def.REQUEST_WRITE;
					}
					
					//if( msg_type == Def.REQUEST_READ || msg_type == Def.REQUEST_WRITE ){// for broadcasting
						
					// create a msg
					Message msg = new Message( msg_type, -1, a, System.currentTimeMillis(), "/" + res_name, null );
					
						//     for this CS request according to the information in 'msg.'
						CriticalSectionRecord cs_record = new CriticalSectionRecord( msg );		
					synchronized( localProcesses.get( a ).cs_my_requests ){
						localProcesses.get( a ).cs_my_requests.add( cs_record );
					}
					
					// **broadcast** the msg (1-to-n)
					msg.set_receiver_id( Def.BROADCAST );
					localProcesses.get( a ).get_postman().send( msg );
				}				
				
				try {
					sleep( 1000 );
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			try {
				sleep( 300 * 1000 );
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			// dump result
			Measure.print_result();
			
			String filename = "";
			String settings = "";
			
			if( Conf.ME_SCHEME == Def.ME_TOKEN ){
				filename = "T";
				settings = "ME=TOKEN";
			}else{
				filename = "TL";
				settings = "ME=TOKENLESS";
			}
			
			filename += "_p" + ( n_node + 1 ) + "t" + n_task + "_" + type + "_r" + Conf.N_RESOURCES_PER_NODE + "__" + ( int )( Math.random() * 4000 ) + ".log";
			settings += " / n_Procs=" + ( n_node + 1 ) + " / n_Tasks=" + n_task + " / n_DS/proc=" + Conf.N_RESOURCES_PER_NODE + "\n";
			
			Measure.result_log( filename, settings );
		}
	}
}//end-of-class